package furnitures 
{
	import app.AppSettings;
	import draw.DrawTool;
	import draw.IPalette;
	import factory.BitmapFactory;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Vector3D;
	import furnitures.parts.Box;
	import furnitures.parts.Handle;
	import gui.PositionInfo;
	import resources.Images;
	import simple3D.core.DisplayObject3D;
	import simple3D.core.ShapeData;
	import simple3D.core.ShapeFactory;
	import simple3D.core.Surface3D;
	import simple3D.materials.BitmapMaterial;
	import simple3D.materials.ColorMaterial;
	import simple3D.materials.Material3D;
	import utils.Logger;
	/**
	 * ...
	 * @author Dmitriy Gaiduk
	 */
	public class Furniture3D extends DisplayObject3D
	{
		private var _surfaces:Vector.<Surface3D>;
		//private var _surfaceUp:Surface3D;
		//private var _surfaceDown:Surface3D;
		private var _edgePoints:Array;
		//private var _furnitureHeight:Number;
		//private var _furnitureWidth:Number = 0;
		private var _model:FurnitureModel;
		//private var _modelOrign:FurnitureModel;
		private var _positionOrign:Vector3D;
		
		public var positionY:Number = 0;
		//public var scaleFactor:Number = 1;
		
		private var _materialSurfaceUp:Material3D;
		private var _materialSurfaceDown:Material3D;
		private var _orignPos:Vector3D;
		
		//private static const DEFAULT_COLOR:uint = 0xEEE6CC;
		private static const DEFAULT_COLOR:uint = 0x29DC10;
		private static const FOOT_WIDTH:Number = 50 * AppSettings.SCALE_FACTOR;
		private static const FOOT_HEIGHT:Number = 100 * AppSettings.SCALE_FACTOR;
		
		private var _scale:Number;
		
		public function Furniture3D(model:FurnitureModel, scaleFactor:Number) 
		{
			//scaleFactor = scaleF;
			//_modelOrign = model.clone();
			_model = model.clone();
			_scale = scaleFactor;
			initFurniture();
			//addEventListener(Event.ADDED_TO_STAGE, handleAddedToStage);
		}
		
		
		private function initFurniture():void 
		{
			//adjustDoorsLines();
			adjustScale();
			log("shape points side: " + _model.shapePointsSide + " depth: " + _model.furnitureDepth);
			adjustDoorsSide();
			switch(_model.shapePointsSide)
			{
				case FurnitureModel.SHAPE_SIDE_FRONT:
						buildFurnitureFromFront();
					break;
				case FurnitureModel.SHAPE_SIDE_TOP:
						buildFurnitureFromTop();
					break;
				case FurnitureModel.SHAPE_SIDE_LEFT:
						buildFurnitureFromLeft();
					break;
				default:
						log("default for: " + _model.shapePointsSide);
						buildFurnitureFromTop();
					break;
			}
			
			// set position
			// on floor
			if (_model.positionY == 0 )
			{
				positionY = -_model.positionY - _model.furnitureHeight;
			}else
			{
				// on wall
				positionY = -_model.positionY;
			}
			
			if (_model.foots != null)
			{
				addFoots();
				moveSurfacesLocal(0, -FOOT_HEIGHT, 0);
			}
		}
		
		private function adjustScale():void 
		{
		
			
			// furnModel.faceAnchorCurve 
			
			_model.furnitureWidth *= _scale;
			_model.furnitureHeight *= _scale;
			_model.furnitureDepth *= _scale;
			_model.positionY *= _scale;
			
			var pointsXY:Array;
			var i:int;
			if(_model.shapePoints!=null){
				scaleArrays(_model.shapePoints);
				/*for (i =0 ; i < _model.shapePoints.length; i++)
				{
					pointsXY = _model.shapePoints[i];
					for (var j:int = 0; j < pointsXY.length; j++)
					{
						pointsXY[j] *= _scale;
					}
				}*/
			}
			
			if (_model.faceAnchorCurve != null)
			{
				_model.faceAnchorCurve.y *= _scale;
			}
			
			// foots
			if (_model.foots != null) {
				for (i = 0 ; i < _model.foots.positions.length; i++ ) {
					 _model.foots.positions[i].x *= _scale;
					 _model.foots.positions[i].y *= _scale;
				}
			}
			
			//handles
			if (_model.faces != null) {
				for (i = 0; i < _model.faces.length; i++ ) {
					 _model.faces[i].doors
					for (var j:int = 0 ; j <  _model.faces[i].doors.length; j++ ) {
						scaleArrays(_model.faces[i].doors[j].doorShape);
						scaleArrays(_model.faces[i].doors[j].lines);
						// handles
						if(_model.faces[i].doors[j].handles!=null){
							for (var k:int = 0; k <  _model.faces[i].doors[j].handles.length; k++ ) {
								scalePositionInfo(_model.faces[i].doors[j].handles[k].posInfo);
							}
						}
					}
				}
			}
			
		}
		
		private function scalePositionInfo(pos:PositionInfo):void {
			pos.posX *= _scale;
			pos.posY *= _scale;
			pos.bottomPadding  *= _scale;
			pos.topPadding  *= _scale;
			pos.leftPadding  *= _scale;
			pos.rightPadding  *= _scale;
		}
		
		private function scaleArrayXY(arr:Array):void {
			if (arr == null ) {
				return;
			}
			for (var i:int = 0; i < arr.length; i++)
			{
				arr[i] *= _scale;
			}
		}
		
		private function scaleArrays(arr:Array):void {
			if (arr == null ) {
				return;
			}
			for (var i:int =0 ; i < arr.length; i++)
			{
				scaleArrayXY(arr[i]);
			}
		}
		
		private function adjustDoorsSide():void 
		{
			if (_model.side == FurnitureModel.SIDE_RIGHT) {
				return;
			}
			log("side: " + _model.side); 
			for each (var face:FurnitureFace in _model.faces)
			{
				if (face.doors != null)
				{
					for each(var door:Box in face.doors)
					{
						for each(var handle:Handle in door.handles) {
								log("Reverse: ");
								handle.posInfo.reverseX();
						}
					}
				}
			}
			
		}
		
		private function adjustDoorsLines():void
		{
			
			for each (var face:FurnitureFace in _model.faces)
			{
				if (face.doors != null)
				{
					for each(var door:Box in face.doors)
					{
						if (door.lines != null)
						{
							door.lines = reversePointsByAxis(door.lines, 1);
							log(getStringFromArr(door.lines));
						}
					}
				}
			}
		}
		
		private function createSurfacesFromTop(furnModel:FurnitureModel):void
		{
			if (furnModel.shapePoints != null && furnModel.shapePoints.length > 0)
			{
				_edgePoints = makeEdgeSideAlign(furnModel.shapePoints);
				log("side align");
				
			}else if (furnModel.faceAnchorCurve != null)
			{
				_edgePoints = makeCurveShape(furnModel);
				log("side curve");
			}else
			{
				_edgePoints = makeRectangleShape(furnModel.furnitureWidth, furnModel.furnitureDepth);
				log("make rect");
			}
			//_edgePoints = furnModel.shapePoints;
			
			_surfaces = new Vector.<Surface3D>();
			var points:Vector.<Number>;
			var pointsXY:Array;
			var prevXY:Array;
			var surf:Surface3D;
			var shapeData:ShapeData;
			var sideInfo:SideInfo;
			// start i=1 - build line with preview point
			prevXY = _edgePoints[0];
			var colorMat:ColorMaterial = new ColorMaterial(DEFAULT_COLOR);
			colorMat.alpha = 0.5;
			for (var i:int = 1; i < _edgePoints.length; i++ )
			{
				
				pointsXY = _edgePoints[i];
				
				sideInfo = new SideInfo();
				if (pointsXY.length == 2)
				{
					points = new Vector.<Number>();
					points.push(prevXY[0],prevXY[1],pointsXY[0],pointsXY[1]);
					shapeData = ShapeFactory.getPlaneDataXY(points, furnitureHeight);
					surf = new Surface3D(shapeData.points, shapeData.indices, shapeData.uvt, 
											colorMat);
					surf.shapeWidth = calcLength(prevXY[0], prevXY[1], pointsXY[0], pointsXY[1]);
					surf.index = i;
					//sideInfo.face = getFaceAtIdex(i);
					sideInfo.type = isFaceIndex(i)? SideInfo.SIDE_TYPE_FACE : SideInfo.SIDE_TYPE_CORPUS;
					
					sideInfo.textureShape = makeRectangleShape(surf.shapeWidth, furnModel.furnitureHeight);
					sideInfo.width = surf.shapeWidth;
					sideInfo.height = furnModel.furnitureHeight;
					surf.userData = sideInfo;
					prevXY = _edgePoints[i];
				}else if (pointsXY.length > 2)
				{
					// add curves
					points = new Vector.<Number>();
					var k:int = 0;
					// make points arr : 3 pair points
					var startXY:Array = [prevXY[0], prevXY[1]];
					while (k < pointsXY.length)
					{
						// pair points {x,y} start, control, end
						points.push(prevXY[0], prevXY[1], pointsXY[k+2], pointsXY[k+3], pointsXY[k], pointsXY[k+1]);
						prevXY = [pointsXY[k],  pointsXY[k + 1]];
						k += 4;
					}
					k -= 4; // setup current k
					// make data
					shapeData = ShapeFactory.getBezierDataMulti(points, furnitureHeight);
					surf = new Surface3D(shapeData.points, shapeData.indices, shapeData.uvt, 
											colorMat);
					surf.shapeWidth = calcLength(startXY[0], startXY[1], pointsXY[k], pointsXY[k+1]);
					surf.index = i;
					sideInfo.face = getFaceAtIdex(i);
					sideInfo.type = isFaceIndex(i)? SideInfo.SIDE_TYPE_FACE : SideInfo.SIDE_TYPE_CORPUS;
					sideInfo.textureShape = makeRectangleShape(surf.shapeWidth, furnModel.furnitureHeight);
					sideInfo.width = surf.shapeWidth;
					sideInfo.height = furnModel.furnitureHeight;
					surf.userData = sideInfo;
				}else
				{
					throw Error("Wrong shape data");
				}
				if (surf != null)
				{
					_surfaces.push(surf);
				}
				surf = null;
			}

			// ===== create up surface
			var upBitmap:BitmapData = BitmapFactory.createShapeColor(_edgePoints, DEFAULT_COLOR, 0.5);
			
			shapeData = ShapeFactory.getPlaneDataXZ(new Point(0, 0), 
													new Point(upBitmap.width, upBitmap.height),
													0);
			surf = new Surface3D(shapeData.points, shapeData.indices, shapeData.uvt, 
											new BitmapMaterial(upBitmap));
			surf.index = -1;
			// create side info
			sideInfo = new SideInfo();
			sideInfo.type = furnModel.hasTabletop ? SideInfo.SIDE_TYPE_TABLETOP : SideInfo.SIDE_TYPE_CORPUS_TOP;
			sideInfo.type = isFaceIndex( -1) ? SideInfo.SIDE_TYPE_FACE : sideInfo.type;
			//sideInfo.type = SideInfo.SIDE_TYPE_CORPUS_TOP;
			sideInfo.textureShape = copyPoints(_edgePoints);
			sideInfo.width = upBitmap.width;
			sideInfo.height = upBitmap.height;
			
			surf.userData = sideInfo;
			
			_surfaces.push(surf);
			
			// =======  create down surface
			shapeData = ShapeFactory.getPlaneDataXZ(new Point(0, 0), 
													new Point(upBitmap.width, upBitmap.height),
													furnitureHeight);
			surf = new Surface3D(shapeData.points, shapeData.indices, shapeData.uvt, 
											new BitmapMaterial(upBitmap));
			sideInfo = new SideInfo();
			sideInfo.type = SideInfo.SIDE_TYPE_CORPUS;
			//sideInfo.type = furnModel.hasTabletop ? SideInfo.SIDE_TYPE_TABLETOP : SideInfo.SIDE_TYPE_CORPUS_TOP;
			
			sideInfo.textureShape =  copyPoints(_edgePoints);
			sideInfo.width = upBitmap.width;
			sideInfo.height = upBitmap.height;
			surf.index = -2;
			
			surf.userData = sideInfo;
			surf.name = "bottom";
			_surfaces.push(surf);
			
			for (i = 0; i < _surfaces.length; i++ )
			{
				addChild(_surfaces[i]);
			}
			
			centrPoint.x = upBitmap.width * 0.5;
			centrPoint.z = upBitmap.height * 0.5;
			centrPoint.y = furnitureHeight * 0.5;
			
		}
		
		private function addFoots():void
		{
			var points:Array = [];
			
			for each(var p:Point in _model.foots.positions)
			{
				points.push([p.x, p.y]);
			}
			
			points = makeEdgeSideAlign(points);
			
			for (var i:int = 0; i < points.length; i++ )
			{
				_model.foots.positions[i].x = points[i][0];
				_model.foots.positions[i].y = points[i][1];
			}
			
			var bottomSurf:Surface3D  = null;
			for each(var surf:Surface3D in _surfaces)
			{
				if (surf.name == "bottom");
				bottomSurf = surf;
				break;
			}
			
			// some fix for z-sorting
			for each(p in _model.foots.positions)
			{
				var foot3D:Foot3D = new Foot3D(FOOT_WIDTH, FOOT_HEIGHT);
				addChild(foot3D);
				foot3D.position.x = p.x;
				foot3D.position.z = p.y;
				foot3D.position.y = furnitureHeight - FOOT_HEIGHT;
				
				// adjust centr point for axis Z. fix for simple calculate distance in DisplayObject3D
				if (bottomSurf != null)
				{
					if ((foot3D.position.z + foot3D.centrPoint.z) > (bottomSurf.position.z + bottomSurf.centrPoint.z))
					{
						foot3D.centrPoint.z -= (foot3D.position.z + foot3D.centrPoint.z - 
													bottomSurf.position.z + bottomSurf.centrPoint.z) +1; 
					}
				}
			}
		}
		
		
		/*override public function sortDepths():void 
		{
				var p3d:Vector3D;
			log("================")
			//super.sortDepths();
			//array to store references to shapes
			var shapes:Array = [];
			//get number of objects in the display list
			var len:uint = this.numChildren;
			//loop thru objects and add Shape3D objects to our array
			for (var i:uint=0;i<len;i++)
			{
				if (this.getChildAt(i) is DisplayObject3D)
					shapes.push(this.getChildAt(i));
			}
			//sort the array based on the z position of the objects
			shapes = shapes.sortOn(["distance"], Array.NUMERIC|Array.DESCENDING);
			//move each object in turn to the top (front) of the display list
			len = shapes.length;
			for (i = 0; i < len; i++)
			{
				if (shapes[i] is Foot3D)
				{
					log("foot distance: " + (shapes[i] as DisplayObject3D).distance);
					p3d = (shapes[i] as DisplayObject3D).centrPoint;
					log("foot centr point x: " + p3d.x + " y: " + p3d.y + " z: " + p3d.z);
					p3d = (shapes[i] as DisplayObject3D).adjustedCentr;
					if (p3d != null)
					{
						log("foot adjusted centr point x: " + p3d.x + " y: " + p3d.y + " z: " + p3d.z);
					}
				}else if (shapes[i] is Surface3D)
				{
					if ((shapes[i] as Surface3D).name == "bottom")
					{
						p3d = (shapes[i] as DisplayObject3D).centrPoint;
						log("bottom centr point x: " + p3d.x + " y: " + p3d.y + " z: " + p3d.z);
						 p3d = (shapes[i] as Surface3D).adjustedCentr;
						log("bottom distance: " +(shapes[i] as Surface3D).distance );
						if (p3d != null)
					{
						log("bottom adjusted centr point x: " + p3d.x + " y: " + p3d.y + " z: " + p3d.z);
					}
					}
				}
				
				this.addChild(shapes[i]);
			}
			
			
				
		}*/
		
		private function moveSurfacesLocal(dx:Number, dy:Number, dz:Number):void
		{
			for each(var surface:Surface3D in _surfaces)
			{
				surface.position.x += dx;
				surface.position.y += dy;
				surface.position.z += dz;
			}
		}
		
		private function buildFurnitureFromTop():void
		{
			createSurfacesFromTop(_model);
			// setup faces
			var sideInfo:SideInfo;
			for each(var surf:Surface3D in _surfaces)
			{
				sideInfo = surf.userData as SideInfo;
				if (sideInfo != null)
				{
					sideInfo.face = getFaceAtIdex(surf.index);
					if (surf.index == -1)
					{
						log("face at -1: " + getFaceAtIdex(surf.index));
					}
				}
				
				if (sideInfo.face != null)
				{
					for each(var door:Box in sideInfo.face.doors)
					{
						if (door.doorShape == null)
						{
							var	doorW:Number = 
								sideInfo.width * door.posRD.x - sideInfo.width * door.positionUV.x;
							var	doorH:Number = 
							sideInfo.height * door.posRD.y - sideInfo.height * door.positionUV.y;
							
							var startX:Number = sideInfo.width * door.positionUV.x;
							var startY:Number = sideInfo.height * door.positionUV.y;
							door.doorShape = makeRectangleShape(doorW, doorH, 0, 0);
						}
					}
				}
			}
		}
		
		private function buildFurnitureFromFront():void
		{
			var tmp:Number;
			// swap depth <=> heigt
			tmp = _model.furnitureDepth;
			_model.furnitureDepth = _model.furnitureHeight;
			_model.furnitureHeight = tmp;
			_model.shapePoints = reversePointsByAxis(_model.shapePoints , 0);
			_model.shapePoints = reversePointsByAxis(_model.shapePoints , 1);
			
			createSurfacesFromTop(_model);
				// create doors, shape for door is main shape. <= builded from front
			var sideInfo:SideInfo;
			for each (var surf:Surface3D in _surfaces)
			{
				sideInfo = surf.userData as SideInfo;
				if (sideInfo != null)
				{
					if (sideInfo.type == SideInfo.SIDE_TYPE_FACE)
					{
						sideInfo.type = SideInfo.SIDE_TYPE_CORPUS;
					}else if (sideInfo.type == SideInfo.SIDE_TYPE_CORPUS_TOP)
					{
						sideInfo.type = SideInfo.SIDE_TYPE_FACE;
						// add faces
						if (_model.faces != null && _model.faces.length > 0)
						{
							sideInfo.face = _model.faces[0];
						}
						if (sideInfo.face.doors != null)
						{
							for each(var door:Box in sideInfo.face.doors)
							{
								if (door.doorShape == null)
								{
									door.doorShape = reversePointsByAxis(sideInfo.textureShape, 1);
								}
							}
						}
					}
				}
				surf.angle.x -= 90 * Math.PI / 180;
				surf.position.z += _model.furnitureHeight;
				surf.angle.z += 180*Math.PI/180;
				surf.position.x += _model.furnitureWidth;
				surf.position.y += _model.furnitureDepth;
			}
			
			// swap depth <=> heigt
			tmp = _model.furnitureDepth;
			_model.furnitureDepth = _model.furnitureHeight;
			_model.furnitureHeight = tmp;
			
			centrPoint.x = _model.furnitureWidth * 0.5;
			centrPoint.z =	_model.furnitureDepth * 0.5;
			centrPoint.y = _model.furnitureHeight * 0.5;
			
		}
		
		private function buildFurnitureFromLeft():void 
		{
			
			// swap width <=> heigt
			var tmp:Number;
			tmp = _model.furnitureWidth;
			_model.furnitureWidth = _model.furnitureHeight;
			_model.furnitureHeight = tmp;
			createSurfacesFromTop(_model);
			
			
			var sideInfo:SideInfo;
			for each(var surf:Surface3D in _surfaces)
			{
				sideInfo = surf.userData as SideInfo;
				if (sideInfo != null)
				{
					sideInfo.face = getFaceAtIdex(surf.index);
				}
				
				if (sideInfo.face != null)
				{
					for each(var door:Box in sideInfo.face.doors)
					{
						if (door.doorShape == null)
						{
							var	doorW:Number = 
								sideInfo.width * door.posRD.x - sideInfo.width * door.positionUV.x;
							var	doorH:Number = 
							sideInfo.height * door.posRD.y - sideInfo.height * door.positionUV.y;
							
							var startX:Number = sideInfo.width * door.positionUV.x;
							var startY:Number = sideInfo.height * door.positionUV.y;
							door.doorShape = makeRectangleShape(doorW, doorH, 0, 0);
						}
					}
				}
				surf.angle.z = -90 * Math.PI / 180;
				surf.position.y = +_model.furnitureWidth;
			}
			
			// status quo. swap depth <=> heigt
			tmp = _model.furnitureWidth;
			_model.furnitureWidth = _model.furnitureHeight;
			_model.furnitureHeight = tmp;
			
		}
		
		private function copyPoints(fromArr:Array):Array
		{
			var shapePoints:Array = [];
			var pointsXY:Array;
			var pointsXYClone:Array;
			for (var i:int = 0; i < fromArr.length; i++)
			{
				pointsXY = fromArr[i];
				pointsXYClone = [];
				for (var j:int = 0; j < pointsXY.length; j++)
				{
					pointsXYClone.push(pointsXY[j]);
				}
				shapePoints.push(pointsXYClone);
			}
			return shapePoints;
		}
		
		private function isFaceIndex(index:int):Boolean
		{
			for each(var face:FurnitureFace in _model.faces)
			{
				if (face.index == index)
				{
					return true;
				}
			}
			return false;
		}
		
		private function getFaceAtIdex(index:int):FurnitureFace
		{
			for each(var face:FurnitureFace in _model.faces)
			{
				if (face.index == index)
				{
					return face.clone();
				}
			}
			return null;
		}
		
		
		
		
		private function makeEdgeSideAlign(edge:Array):Array
		{
			log("side align: " +  _model.side);
			if (_model.side == FurnitureModel.SIDE_RIGHT)
			{
				return edge;
			}else if (_model.side != FurnitureModel.SIDE_LEFT)
			{
				return edge;
			}
			
			log("setup side align");
			var maxX:Number = 0;
			var minX:Number = Number.MAX_VALUE;
			var points:Array = [];
			var pointsXY:Array;
			var pointsXYAdjusted:Array;
			var i:int;
			var j:int;
			for (i = 0; i < edge.length; i++ )
			{
				pointsXY = edge[i];
				for (j = 0; j < pointsXY.length; j += 2 )
				{
					maxX = Math.max(maxX, pointsXY[j]);
					minX = Math.min(minX, pointsXY[j]);
				}
			}
			
			var shapeWidth:Number = maxX - minX;
			
			for (i = 0; i < edge.length; i++ )
			{
				pointsXY = edge[i];
				pointsXYAdjusted = [];
				
				for (j = 0; j < pointsXY.length; j += 2 )
				{
					pointsXYAdjusted.push(maxX - pointsXY[j] + minX);
					pointsXYAdjusted.push(pointsXY[j+1]);
				}
				
				points.push(pointsXYAdjusted);
			}
			
			return points;
		}
		
		/**
		 * 
		 * @param	edge
		 * @param	axis 0 - x, 1 - y
		 * @return
		 */
		private function reversePointsByAxis(edge:Array, axis:int):Array
		{
			log("Reverse");
			var maxValue:Number = 0;
			var minValue:Number = Number.MAX_VALUE;
			var points:Array = [];
			var pointsXY:Array;
			var pointsXYAdjusted:Array;
			var i:int;
			var j:int;
			for (i = 0; i < edge.length; i++ )
			{
				pointsXY = edge[i];
				for (j = 0; j < pointsXY.length; j += 2 )
				{
					maxValue = Math.max(maxValue, pointsXY[j + axis]);
					minValue = Math.min(minValue, pointsXY[j + axis]);
				}
			}
			
			var shapeSizeByAxis:Number = maxValue - minValue;
			
			for (i = 0; i < edge.length; i++ )
			{
				pointsXY = edge[i];
				pointsXYAdjusted = [];
				
				for (j = 0; j < pointsXY.length; j += 2 )
				{
					if (axis == 0)
					{
						pointsXYAdjusted.push(maxValue - pointsXY[j] + minValue);
						pointsXYAdjusted.push(pointsXY[j + 1]);
					}else
					{
						pointsXYAdjusted.push(pointsXY[j]);
						pointsXYAdjusted.push(maxValue - pointsXY[j + 1] + minValue);
					}
				}
				
				points.push(pointsXYAdjusted);
			}
			
			log("max: " + maxValue);
			return points;
		}
		
		
		private function makeRectangleShape(rectWidth:Number, rectHeight:Number, 
											startX:Number = 0,
											startY:Number = 0 ):Array
		{
			var points:Array = [];
			points.push([startX, startY]);
			points.push([startX+ rectWidth, startY]);
			points.push([startX + rectWidth, startY + rectHeight]);
			points.push([startX, startY + rectHeight]);
			points.push([startX, startY]);
			return points;
		}
		
		private function makeCurveShape(model:FurnitureModel):Array
		{
			var points:Array = [];
			points.push([0, 0]);
			points.push([model.furnitureWidth, 0]);
			points.push([model.furnitureWidth, model.furnitureDepth]);
			points.push([0, model.furnitureDepth, 
						 model.faceAnchorCurve.x * model.furnitureWidth, 
						 model.furnitureDepth + model.faceAnchorCurve.y]);
			points.push([0, 0]);
			return points;
		}
		
		private function calcLength(x1:Number, y1:Number, x2:Number, y2:Number):Number
		{
			return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1)*(y2 - y1));
		}
		
		
		public function setCorpusPalette(palette:IPalette):void
		{
			if (palette == null)
			{
				return;
			}
			var shape:Shape = new Shape();
			var gr:Graphics = shape.graphics;
			var bm:BitmapData;
			var sideInfo:SideInfo;
			
			// обход всех поверхностей
			for each(var surface:Surface3D in _surfaces)
			{
				sideInfo = surface.userData as SideInfo;
				if (sideInfo == null)
				{
					log("setCorpusPalette. side info null");
					continue;
				}
				// нужны только поверхности для корпуса
				if (!(sideInfo.type == SideInfo.SIDE_TYPE_CORPUS || sideInfo.type == SideInfo.SIDE_TYPE_CORPUS_TOP))
				{
					continue;
				}
				
				gr.clear();
				palette.beginFill(gr, "", sideInfo.width, sideInfo.height);
				DrawTool.drawEdge(gr, sideInfo.textureShape);
				palette.endFill(gr);
				var shapeHolder:Sprite = new Sprite();
				shapeHolder.addChild(shape);
				// reverse by Y axis
				shape.scaleY = -1;
				shape.y = sideInfo.height;//  excluding line width - not shape.height!!
				
				bm = new BitmapData(sideInfo.width, sideInfo.height, true, 0xFF0000);
				bm.draw(shapeHolder, null, null, null, null, true);
				surface.material = new BitmapMaterial(bm);
			}
		}
		
		public function setFacadePalette(facadePalette:IPalette, techPalette:IPalette):void
		{
			log("set facade: " + facadePalette + " techpal: " + techPalette);
			var shape:Sprite = new Sprite();
			var gr:Graphics = shape.graphics;
			var bm:BitmapData;
			var sideInfo:SideInfo;
			
			// обход всех поверхностей
			for each(var surface:Surface3D in _surfaces)
			{
				sideInfo = surface.userData as SideInfo;
				if (sideInfo == null)
				{
					log("setFacadePalette. side info null");
					continue;
				}
				// нужны только поверхности для фасада
				if (sideInfo.type != SideInfo.SIDE_TYPE_FACE)
				{
					continue;
				}
				
					var face:FurnitureFace = sideInfo.face;
					// draw face
					gr.clear();
					//draw doors
					var doorW:Number;
					var doorH:Number;
					if (face == null)
					{
						continue;
					}
					var reverseByX:Boolean = _model.side == FurnitureModel.SIDE_LEFT? true : false;
					for each(var door:Box in face.doors)
					{
						
						var doorShape:Shape = new Shape();
						doorW = sideInfo.width * door.posRD.x - sideInfo.width * door.positionUV.x;
						doorH = sideInfo.height * door.posRD.y - sideInfo.height * door.positionUV.y;
						var startX:Number = sideInfo.width * door.positionUV.x;
						var startY:Number = sideInfo.height * door.positionUV.y;
						
						if (door.type == Box.TYPE_TECH && techPalette!=null) {
							techPalette.beginFill(doorShape.graphics, _model.getDoorSize(door), doorW, doorH,
												reverseByX);
						}else if(facadePalette!=null){
							
							facadePalette.beginFill(doorShape.graphics, _model.getDoorSize(door), doorW, doorH,
											reverseByX);
						}
						/*gr.drawRect(sideInfo.width * door.positionUV.x, 
									sideInfo.height * door.positionUV.y, 
									doorW, doorH);*/
						DrawTool.drawEdge(doorShape.graphics, door.doorShape);
						
						if (door.type == Box.TYPE_TECH && techPalette!=null) {
							techPalette.endFill(doorShape.graphics);
						}else if(facadePalette != null){
							facadePalette.endFill(doorShape.graphics);
						}
						doorShape.x = startX;
						doorShape.y = startY;
						
						// draw lines
						if (door.lines != null)
						{
							if (facadePalette!= null && facadePalette.lineStyle(gr))
							{
							//doorShape.graphics.lineStyle(10, 0xFF0000);
								DrawTool.drawLines(doorShape.graphics, door.lines);
							}
						}
						
						// draw handle
						/*if (door.handle != null)
						{
							var handleName:String = door.handle.handleType == Handle.HANDLE_VERTICAL ?
													Images.HANDLE_VERT : Images.HANDLE_HORIZ;
							var bmData:BitmapData = Images.getImageBitmap(handleName);
							if (bmData != null)
							{
								log("Draw handle: " + handleName);
								var matr:Matrix = new Matrix();
								matr.tx = door.handle.posInfo.getPosX(doorW, bmData.width);
								matr.ty = door.handle.posInfo.getPosY(doorH, bmData.height);
								doorShape.graphics.lineStyle();
								doorShape.graphics.beginBitmapFill(bmData, matr, false, true);
								doorShape.graphics.drawRect(matr.tx, matr.ty, bmData.width, bmData.height);
								doorShape.graphics.endFill();
							}
						}*/
						if (door.handles != null)
						{
							for each(var handle:Handle in door.handles)
							{
								var handleName:String = handle.handleType == Handle.HANDLE_VERTICAL ?
														Images.HANDLE_VERT : Images.HANDLE_HORIZ;
								var bmData:BitmapData = Images.getImageBitmap(handleName);
								if (bmData != null)
								{
									//log("Draw handle: " + handleName);
									var matr:Matrix = new Matrix();
									matr.tx = handle.posInfo.getPosX(doorW, bmData.width);
									matr.ty = handle.posInfo.getPosY(doorH, bmData.height);
									doorShape.graphics.lineStyle();
									doorShape.graphics.beginBitmapFill(bmData, matr, false, true);
									doorShape.graphics.drawRect(matr.tx, matr.ty, bmData.width, bmData.height);
									doorShape.graphics.endFill();
								}
							}
						}
						shape.addChild(doorShape);
					}
					
					var shapeHolder:Sprite = new Sprite();
					shapeHolder.addChild(shape);
					// reverse by Y axis
					//shape.scaleY = -1;
					//shape.y = sideInfo.height; //  excluding line width - not shape.height!!
					bm = new BitmapData(sideInfo.width, sideInfo.height, true, 0xFF0000);
					bm.draw(shapeHolder, null, null, null, null, true);
					surface.material = new BitmapMaterial(bm);
			}
		}
		
		
		
		// for test. print array
		private function getStringFromArr(edge:Array):String
		{
			var str:String = "";
			var pointsXY:Array;
			for (var i:int = 0; i < edge.length; i++ )
			{
				pointsXY = edge[i];
				str += ", [";
				for (var j:int = 0; j < pointsXY.length; j += 2 )
				{
					if (j > 0)
					{
						str += ", ";
					}
					str += " " + pointsXY[j];
					str += ", " + pointsXY[j+1];
				}
				str += "]";
			}
			
			return str;
		}
		
		public function setTabletopPalette(palette:IPalette):void
		{
			if (palette == null)
			{
				return;
			}
			
			var shape:Shape = new Shape();
			var gr:Graphics = shape.graphics;
			var bm:BitmapData;
			var sideInfo:SideInfo;
			
			// обход всех поверхностей
			for each(var surface:Surface3D in _surfaces)
			{
				sideInfo = surface.userData as SideInfo;
				if (sideInfo == null)
				{
					log("setTabletopPalette. side info null");
					continue;
				}
				// нужны только поверхности для стола
				if (sideInfo.type != SideInfo.SIDE_TYPE_TABLETOP)
				{
					continue;
				}
				
				gr.clear();
				palette.beginFill(gr, "", sideInfo.width, sideInfo.height);
				DrawTool.drawEdge(gr, sideInfo.textureShape);
				palette.endFill(gr);
				
				var shapeHolder:Sprite = new Sprite();
				shapeHolder.addChild(shape);
				// reverse by Y axis
				shape.scaleY = -1;
				shape.y = sideInfo.height;//  excluding line width - not shape.height!!
				
				bm = new BitmapData(sideInfo.width, sideInfo.height, true, 0xFF0000);
				bm.draw(shapeHolder, null, null, null, null, true);
				surface.material = new BitmapMaterial(bm);
			}
		}
		
		public function get furnitureHeight():Number 
		{
			return _model.furnitureHeight;
		}
		
		public function get furnitureWidth():Number 
		{
			return _model.furnitureWidth;
		}
		
		public function get furnitureDepth():Number 
		{
			return _model.furnitureDepth;
		}
		
		private function log(msg:String):void
		{
			Logger.log(this, msg);
		}
		
		public function getSizesTexture():Vector.<String>
		{
			return _model.getSizesNeeded();
		}
		
		public function get itemType():String 
		{
			return _model.itemType;
		}
		
		
	}

}